﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using Unity.Collections;
public class Shadows
{

    const string bufferName = "Shadows";

    static int shadowMapId = Shader.PropertyToID("_ShadowMap");
    static int worldToShadowMatrixId = Shader.PropertyToID("_WorldToShadowMatrix");
    static int shadowBiasId = Shader.PropertyToID("_ShadowBias");
    static int shadowStrengthId = Shader.PropertyToID("_ShadowStrength");


    CommandBuffer _shadowBuffer = new CommandBuffer()
    {
        name = bufferName
    };

    private ScriptableRenderContext _context;
    private CullingResults _cullingResults;
    private ShadowSettings _shadowSettings;

    private RenderTexture _shadowMap;
    private Matrix4x4 viewMatrix, projMatrix;

    public void Setup(ScriptableRenderContext sc, CullingResults cr, ShadowSettings ss)
    {
        _context = sc;
        _cullingResults = cr;
        _shadowSettings = ss;
        _shadowSettings.Setup(_cullingResults.visibleLights);
        CreateShadowMap();
    }


    private void ExecuteBuffer()
    {
        _context.ExecuteCommandBuffer(_shadowBuffer);
        _shadowBuffer.Clear();
    }

    private void CreateShadowMap()
    {
        int mapSize = (int)_shadowSettings.ShadowTextureSize;
        int depthSize = (int)_shadowSettings.ShadowTextureDepthSize;
        _shadowMap = RenderTexture.GetTemporary(mapSize, mapSize, depthSize, RenderTextureFormat.Shadowmap);
        _shadowMap.filterMode = FilterMode.Bilinear;
        _shadowMap.wrapMode = TextureWrapMode.Clamp;
    }


    public void CleanUp()
    {
        if(_shadowMap != null)
        {
            RenderTexture.ReleaseTemporary(_shadowMap);
            _shadowMap = null;
        }
    }

    public void RenderShadow(int index)
    {
        CoreUtils.SetRenderTarget(_shadowBuffer, _shadowMap,
            RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, ClearFlag.Depth);
        ShadowSplitData _splitData;
        _cullingResults.ComputeSpotShadowMatricesAndCullingPrimitives(index, out viewMatrix, out projMatrix, out _splitData);
        _shadowBuffer.SetViewProjectionMatrices(viewMatrix, projMatrix);

        _shadowBuffer.SetGlobalFloat(shadowBiasId, _shadowSettings.GetShadowBias(index));
        _shadowBuffer.SetGlobalFloat(shadowStrengthId, _shadowSettings.GetShadowStrength(index));
        ExecuteBuffer();
        var shadowSetting = new ShadowDrawingSettings(_cullingResults, index);
        _context.DrawShadows(ref shadowSetting);
        _shadowBuffer.EndSample(bufferName);
        ExecuteBuffer();
    }

    public void SendToGPU()
    {
        
        if (SystemInfo.usesReversedZBuffer)
        {
            projMatrix.m20 = -projMatrix.m20;
            projMatrix.m21 = -projMatrix.m21;
            projMatrix.m22 = -projMatrix.m22;
            projMatrix.m23 = -projMatrix.m23;
        }

        var scaleOffset = Matrix4x4.identity;
        scaleOffset.m00 = scaleOffset.m11 = scaleOffset.m22 = 0.5f;
        scaleOffset.m03 = scaleOffset.m13 = scaleOffset.m23 = 0.5f;

        Matrix4x4 worldToShadowMatrix = scaleOffset * projMatrix * viewMatrix;
        _shadowBuffer.SetGlobalMatrix(worldToShadowMatrixId, worldToShadowMatrix);
        _shadowBuffer.SetGlobalTexture(shadowMapId, _shadowMap);
        ExecuteBuffer();
    }


}
